Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HW 2 init #5

Open
wants to merge 6 commits into
base: amos
Choose a base branch
from
Open

HW 2 init #5

wants to merge 6 commits into from

Conversation

adkron
Copy link
Collaborator

@adkron adkron commented Nov 20, 2018

I have an issue with buildMessage not being exhaustive because it
needs to match on an empty map. I really didn't like building all this
stuff in the case statement and that is why I pulled it out. I think I
need to start thinking of functions returning functions and build it up
as arguments get passed in.

Amos King @adkron [email protected]

adkron and others added 3 commits November 19, 2018 14:17
When working through the code it is often nice to be able to run a single test.

Amos King @adkron <[email protected]>
Update Readme to show how to run a single test
I have an issue with `buildMessage` not being exhaustive because it
needs to match on an empty map. I really didn't like building all this
stuff in the case statement and that is why I pulled it out. I think I
need to start thinking of functions returning functions and build it up
as arguments get passed in.

Amos King @adkron <[email protected]>

parse :: String -> [LogMessage]
parse _ = []
parse = map (parseMessage) . lines
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Drop the parentheses

_ -> Unknown s

buildMessage :: MessageType -> [String] -> LogMessage
buildMessage m (t:ws) = LogMessage m (read t) (unwords ws)
Copy link

@glguy glguy Nov 21, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If your function only makes sense for non-empty lists, it can be better to instead take the first element as a distinct argument:

buildMessage :: MessageType -> String -> [String] -> LogMessage

and then put the logic for what to do when you don't have one in the caller

"W":w:ws -> buildMessage Warning w ws

parseMessage s = Unknown s
parseMessage s =
case words s of
"E":i:ws -> buildMessage (Error (read i)) ws
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

read can be OK when you're sure that the input string will parse, or when you're doing some temporary debugging code, but if you want to detect parse failures check out Text.Read.readMaybe :: Read a => String -> Maybe a

@@ -6,22 +6,44 @@ module Cis194.Hw.LogAnalysis where
import Cis194.Hw.Log
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A note about a flaw in this exercise: LogMessage shouldn't have an Unknown constructor. Error handling like this should be done in parallel to the type for valid log messages. At a minimum you might have: Either String LogMessage or something. This causes problems later in the exercises when you have to ignore Unknown.


insert :: LogMessage -> MessageTree -> MessageTree
insert (Unknown _) t = t
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As written this pattern is just fine, but a neat thing you can use is record constructor syntax to match a constructor where you don't care about any of its fields. This pays off more for constructors with more fields:

insert Unknown{} t = t

insert _ t = t

build :: [LogMessage] -> MessageTree
build _ = Leaf
build = foldr insert Leaf . reverse
Copy link

@glguy glguy Nov 21, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

foldr combined with reverse behaves like a foldl with an extra pass.

whatWentWrong _ = []
whatWentWrong = map getString . filter isSever . inOrder . build

isSever :: LogMessage -> Bool
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

severe :-)


inOrder :: MessageTree -> [LogMessage]
inOrder _ = []
inOrder (Node l m r) = inOrder l ++ [m] ++ inOrder r
inOrder Leaf = []
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This implements the algorithm as specified, but it is somewhat inefficient to left-nest ++ as each nested use must be deconstructed by the next ++. A common pattern for this is:

inOrder :: MessageTree -> [LogMessage]
inOrder t = go t []
  where
    go (Node l m r) = inOrder l . (m:) . inOrder r
    go Leaf = id

This pattern is captured by the dlist package as well as the ShowS type in base.

@adkron
Copy link
Collaborator Author

adkron commented Nov 21, 2018 via email

@adkron
Copy link
Collaborator Author

adkron commented Nov 21, 2018 via email

I really like the pattern that was used in `inOrder` that was brilliant. Thanks
for the help and the pointer on matching a type constructor.

Amos King @adkron <[email protected]>
foldl and foldr expect arguments in different orders, but flip allows the
arguments to be flipped.

Amos King @adkron <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants